if (nv.CreateNamespace("nv.String")) {
    nv.String = new function () {
        this.Truncate = function (str, num) {
            var returnStr = str;
            if (str.length > num) returnStr = str.substring(0, num - 3) + "...";
            return returnStr;
        };
        this.Trim = function (str) {
            if (typeof (str) == "string") return str.replace(/^\s+|\s+$/g, "");
            else return str;
        };
        this.LTrim = function (str) {
            if (typeof (str) == "string") return str.replace(/^\s+/, "");
            else return str;
        };
        this.RTrim = function (str) {
            if (typeof (str) == "string") return str.replace(/\s+$/, "");
            else return str;
        };
        this.toProperCase = function (str) {
            return str.charAt(0).toUpperCase() + str.substring(1, str.length).toLowerCase();
        };
        var regexpSpecials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g");
        this.RegExpEscape = function (str) {
            return str.replace(regexpSpecials, "\\$&");
        };
        this.Repeat = function (str, times) {
            var result = "";
            for (var i = 0; i < times; i++) {
                result += str;
            }
            return result;
        };
        this.Print = function (text) {
            if (typeof (text) !== "string") {
                return null;
            }
            for (var i = 1; i < arguments.length; i++) {
                text = text.replace(eval("/\\$" + i + "/g"), arguments[i]);
            }
            return text;
        };
        this.Diff = function (left, right, applyClass) {
            if (!left) {
                return right;
            }
            var diffStartIndex = 0;
            var minLength = Math.min(left.length, right.length);
            while (diffStartIndex < minLength) {
                if (right.charAt(diffStartIndex) != left.charAt(diffStartIndex)) {
                    break;
                }
                diffStartIndex++;
            }
            if (diffStartIndex == minLength && left.length == right.length) {
                return left;
            } else {
                return right.substring(0, diffStartIndex) + "<span class='" + applyClass + "'>" + right.substring(diffStartIndex) + "</span>";
            }
        };
        this.GetHashCode = function (text) {
            var algo = {
                fnv: function (buf, offset, len, seed) {
                    for (var i = offset; i < offset + len; i++) {
                        seed ^= buf[i];
                        seed += (seed << 1) + (seed << 4) + (seed << 7) + (seed << 8) + (seed << 24);
                    }
                    return seed;
                },
                getHash: function (hash) {
                    return Number(hash & 0x00000000ffffffff).toString(16);
                },
                INIT: 0x811c9dc5
            };
            var FNV1a32 = function () {
                var hash = 0;
                var fnv = algo.fnv;
                var getHashValue = algo.getHash || function (h) {
                        return Number(h).toString(16);
                    };
                var INIT = algo.INIT;

                function getBytes(s) {
                    var buf = [];
                    for (var i = 0; i < s.length; i++) {
                        buf.push(s.charCodeAt(i));
                    }
                    return buf;
                }
                return {
                    init: function (buf, offset, len) {
                        if (typeof buf == "string") {
                            buf = getBytes(buf);
                            offset = 0;
                            len = buf.length;
                        }
                        hash = fnv(buf, offset, len, INIT);
                    },
                    update: function (buf, offset, len) {
                        if (typeof buf == "string") {
                            buf = getBytes(buf);
                            offset = 0;
                            len = buf.length;
                        }
                        hash = fnv(buf, offset, len, hash);
                    },
                    getHash: function () {
                        return getHashValue(hash);
                    }
                };
            };
            var fnv132 = new FNV1a32();
            fnv132.init(text);
            return fnv132.getHash();
        };
        this.stripNonNumeric = function (str) {
            var result = new String(str);
            result = result.replace(/[^0-9]/g, '');
            return result;
        };
        this.wordCut = function (stringToCut, totalLength, suffixToAppend) {
            if (stringToCut.length <= totalLength) return stringToCut;
            if (suffixToAppend == null) suffixToAppend = "...";
            return stringToCut.substr(0, totalLength - suffixToAppend.length) + suffixToAppend;
        };
        this.approximateWordBoundaryCut = function (stringToCut, totalLength) {
            "use strict";
            var nextSpace = stringToCut.indexOf(" ", totalLength);
            if (nextSpace == -1) return stringToCut;
            return stringToCut.substr(0, nextSpace);
        };
        this.wordBoundaryCut = function (stringToCut, totalLength, suffixToAppend) {
            "use strict";
            if (stringToCut.length <= totalLength) return stringToCut;
            var fragments = stringToCut.split(" ");
            suffixToAppend = suffixToAppend || "";
            totalLength -= suffixToAppend.length;
            var i = 0;
            var lengthSoFar = totalLength;
            while (lengthSoFar > 0) {
                if (i > 0) lengthSoFar -= 1;
                lengthSoFar -= fragments[i++].length;
            }
            return fragments.slice(0, Math.max(1, i - 1)).join(" ") + suffixToAppend;
        };
    };
}